use std::ffi::{CStr, CString};
use std::fmt::{Debug, Display, format, Formatter};
use std::fmt;
use std::ops::Deref;
use std::os::raw::{c_char, c_int, c_long, c_void};
use std::ptr;

use base64::{decode, encode};
use opencv::{core, Error};
use opencv::core::{_InputArray, _InputArrayTrait, Mat, MatTrait, MatTraitManual, Rect as OpencvRect, Size, Vector};
use opencv::highgui;
use opencv::highgui::named_window;
use opencv::imgcodecs;
use opencv::imgcodecs::IMREAD_COLOR;
use opencv::types::VectorOfu8;
//先不封装
// pub struct MultiFace {
//     pub faceRect: *mut MRECT,
//     pub faceOrient: *mut MInt32,
//     pub faceNum: MInt32,
//     pub faceID: *mut MInt32,
// }
use serde::{Deserialize, Serialize};

use crate::bindings::{ASF_AGE, ASF_AgeInfo, ASF_CompareModel_ASF_LIFE_PHOTO, ASF_DetectMode_ASF_DETECT_MODE_IMAGE, ASF_DetectModel_ASF_DETECT_MODEL_RGB, ASF_FACE3DANGLE, ASF_Face3DAngle, ASF_FACE_DETECT, ASF_FaceFeature, ASF_FACERECOGNITION, ASF_GENDER, ASF_GenderInfo, ASF_IR_LIVENESS, ASF_LIVENESS, ASF_LivenessInfo, ASF_LivenessThreshold, ASF_MultiFaceInfo, ASF_OrientPriority_ASF_OP_0_ONLY, ASF_SingleFaceInfo, ASF_VERSION, ASFDetectFaces, ASFFaceFeatureCompare, ASFFaceFeatureExtract, ASFGetAge, ASFGetFace3DAngle, ASFGetGender, ASFGetLivenessScore, ASFGetVersion, ASFInitEngine, ASFOnlineActivation, ASFProcess, ASFSetLivenessParam, ASFUninitEngine, ASVL_PAF_RGB24_B8G8R8, MByte, MERR_ASF_ALREADY_ACTIVATED, MFloat, MInt32, MRECT};

static MOK: c_long = 0;

// 虹软引擎包装
// pub struct ArcEngine {
//     engine: Box<*mut c_void>,
// }
pub struct ArcEngine_U {
    engine: usize,
}

impl Drop for ArcEngine_U {
    fn drop(&mut self) {
        unsafe {
            let engine = self.engine as *const c_void as *mut c_void;
            ASFUninitEngine(engine);
        }
    }
}

pub struct ArcEngine {
    engine: Box<*mut c_void>,
}


pub type FaceResult<T> = Result<T, String>;


/// 检测软件是否联网激活过
/// 如果引擎没有激活，调用此方法可以生成激活文件
///```
///     use arc_lib::sdk::check_arc_active;
///     let actived = check_arc_active("FnumvuHSGHnkiyeHpq9uhv574HkMcX2mroX73W4z2CHg", "DudLoL4ATjvGNB2L59zj4rEFEPCGEwoJAMPnamfHL4Mw");
///     if let Err(err_msg) = actived {
///         println!("active fail:{}", err_msg);
///     } else {
///         println!("active success");
///     }
///```
pub fn check_arc_active<S: Into<String>>(app_id: S, sdk_key: S) -> FaceResult<()> {
    unsafe {
        let mut app_id = CString::new(app_id.into()).expect("CString::new failed");
        let app_id = app_id.into_raw();
        let mut sdk_key = CString::new(sdk_key.into()).expect("CString::new failed");
        let sdk_key = sdk_key.into_raw();
        //激活接口,首次激活需联网
        let res = ASFOnlineActivation(app_id, sdk_key);
        //释放资源
        Box::from_raw(app_id);
        Box::from_raw(sdk_key);
        if 0 != res && MERR_ASF_ALREADY_ACTIVATED != res as u32 {
            Err(format!("ASFActivation fail: {}", res))
        } else {
            Ok(())
        }
    }
}

#[test]
pub fn test_check_arc_active() {
    let actived = check_arc_active("FnumvuHSGHnkiyeHpq9uhv574HkMcX2mroX73W4z2CHg", "DudLoL4ATjvGNB2L59zj4rEFEPCGEwoJAMPnamfHL4Mw");
    if let Err(err_msg) = actived {
        println!("active fail:{}", err_msg);
    } else {
        println!("active success");
    }
}

/// 打印arc_sdk的版本
///```
/// use arc_lib::sdk;
/// let version = sdk::arc_sdk_version();
/// version.map(|v| {
/// println!("Version:{}", v.version);
/// println!("BuildDate:{}", v.build_date);
/// println!("CopyRight:{}", v.copy_right);
/// });
///```
pub fn arc_sdk_version() -> FaceResult<ArcVersion> {
    unsafe {
        let v = ASFGetVersion();
        if ptr::null() != v.Version {
            let version = format!("{:?}", CStr::from_ptr(v.Version));
            let build_date = format!("{:?}", CStr::from_ptr(v.BuildDate));
            let copy_right = format!("{:?}", CStr::from_ptr(v.CopyRight));
            Ok(ArcVersion {
                version,
                build_date,
                copy_right,
            })
        } else { Err(String::from("未获取到版本信息")) }
    }
}

/// arc version
pub struct ArcVersion {
    pub version: String,
    pub build_date: String,
    pub copy_right: String,
}

/// 人脸特征实体
pub struct FaceFeature {
    pub feature: Vec<u8>,
}

impl FaceFeature {
    fn to_asf_face_feature(&self) -> ASF_FaceFeature {
        let ptr = self.feature.as_ptr() as *const MByte as *mut MByte;
        ASF_FaceFeature { feature: ptr, featureSize: self.feature.len() as i32 }
    }
}

/// 人脸3D角度信息
pub struct Face3DAngle {
    roll: f32,
    yaw: f32,
    pitch: f32,
}

impl Display for Face3DAngle {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "roll {},yaw {},pitch {}", self.roll, self.yaw, self.pitch)
    }
}

#[test]
pub fn test_version() {
    let version = arc_sdk_version();
    version.map(|v| {
        println!("Version:{}", v.version);
        println!("BuildDate:{}", v.build_date);
        println!("CopyRight:{}", v.copy_right);
    });
}

impl ArcEngine_U {
    /// 初始引擎
    pub fn init_engine() -> FaceResult<ArcEngine_U> {
        unsafe {
            //初始化接口
            let mask = ASF_FACE_DETECT | ASF_FACERECOGNITION | ASF_AGE | ASF_GENDER | ASF_FACE3DANGLE |
                ASF_LIVENESS | ASF_IR_LIVENESS;//配置需要的功能函数
            let engine = Box::into_raw(Box::new(ptr::null_mut() as *mut c_void));
            // let engine = ptr::null_mut() as *mut c_void;
            // let engine = &engine as *const *mut c_void as *mut *mut c_void;
            let res = ASFInitEngine(ASF_DetectMode_ASF_DETECT_MODE_IMAGE, ASF_OrientPriority_ASF_OP_0_ONLY, 30, 5, mask as c_int,
                                    engine);
            let engine = Box::from_raw(engine);//靠所有权释放这个指针
            if res != MOK {
                Err(format!("ASFInitEngine 1 fail: {}", res))
            } else {
                let e = *engine;
                Ok(ArcEngine_U { engine: e as usize })
            }
        }
    }
}

impl ArcEngine {
    /// 初始引擎
    pub fn init_engine(engine: &ArcEngine_U) -> ArcEngine {
        let engine = Box::new(engine.engine as *const c_void as *mut c_void);
        ArcEngine { engine }
    }
    /// 获取照片中的人脸信息
    pub fn face_check(&self, face_pic: &mut Mat) -> FaceResult<Vec<FaceInfo>> {
        let detectedFaces = Box::new(ASF_MultiFaceInfo { faceRect: ptr::null_mut(), faceOrient: ptr::null_mut(), faceNum: 0, faceID: ptr::null_mut() });
        unsafe {
            let i_ptr = face_pic.data_mut() as *mut u8;
            let size = face_pic.size().unwrap();// TODO Mat属性
            let detectedFaces = Box::into_raw(detectedFaces);
            let res = ASFDetectFaces(*self.engine, size.width, size.height, ASVL_PAF_RGB24_B8G8R8 as i32,
                                     i_ptr, detectedFaces,
                                     ASF_DetectModel_ASF_DETECT_MODEL_RGB);
            if res == MOK {
                let detectedFaces = Box::from_raw(detectedFaces);
                Ok(Into::into(*detectedFaces))
            } else {
                Err(format!("arc error code {}", res))
            }
        }
    }

    /// 根据人脸信息和原始图片获取出人脸特征
    pub fn face_feature(&self, face_img: &mut Mat, face_info: &FaceInfo) -> FaceResult<Box<FaceFeature>> {
        let mut feature1 = ASF_FaceFeature { feature: ptr::null_mut(), featureSize: 0 };
        let res = unsafe {
            let info = &ASF_SingleFaceInfo { faceRect: face_info.faceRect.into(), faceOrient: face_info.orient } as *const ASF_SingleFaceInfo as *mut ASF_SingleFaceInfo;
            let feature_ptr = &feature1 as *const ASF_FaceFeature as *mut ASF_FaceFeature;
            ASFFaceFeatureExtract(*self.engine, face_img.cols(), face_img.rows(), ASVL_PAF_RGB24_B8G8R8 as i32,
                                  face_img.data_mut(), info, feature_ptr)
        };
        if res == MOK {
            unsafe {
                //拷贝feature
                let copyfeature1 = feature1;//rust直接copy TODO 这段代码是为了以后 使用，所以在此多一步来复制 copy_from_slice
                let len = copyfeature1.featureSize as usize;
                let features = ptr::slice_from_raw_parts(copyfeature1.feature, len);
                if let Some(val_back) = features.as_ref() {
                    let feature = val_back.clone().to_vec();
                    Ok(Box::new(FaceFeature { feature }))
                } else {
                    Err(format!("Face Feature Extract 1 fail: {}", "sdk ptr convert error"))
                }
            }
        } else {
            Err(format!("Face Feature Extract 1 fail: {}", res))
        }
    }


    pub fn process(&self, img: &mut Mat, face_infos: &Vec<FaceInfo>) -> FaceResult<()> {
        let mut threshold = ASF_LivenessThreshold { thresholdmodel_BGR: 0.5, thresholdmodel_IR: 0.7 };
        unsafe {
            //设置活体置信度 SDK内部默认值为 IR：0.7  RGB：0.5（无特殊需要，可以不设置）
            let res = ASFSetLivenessParam(*self.engine, &threshold as *const ASF_LivenessThreshold as *mut ASF_LivenessThreshold);
            if res != MOK {
                return Err(format!("ASFSetLivenessParam fail: {}", res));
            } else {
                println!("RGB Threshold: {:?}  IR Threshold: {:?}", threshold.thresholdmodel_BGR, threshold.thresholdmodel_IR);
                println!("\n*************** RGB Process ***************\n");
                // RGB图像检测
                let processMask = ASF_AGE | ASF_GENDER | ASF_FACE3DANGLE | ASF_LIVENESS;

                let info: ASF_MultiFaceInfo = ASF_MultiFaceInfo::from(face_infos);
                let res = ASFProcess(*self.engine, img.cols(), img.rows(), ASVL_PAF_RGB24_B8G8R8 as i32,
                                     img.data_mut(), &info as *const ASF_MultiFaceInfo as *mut ASF_MultiFaceInfo, processMask as i32);
                info.drop();//rust 分配 rust释放
                if res != MOK {
                    Err(format!("ASFProcess fail: {}", res))
                } else {
                    // println!("ASFProcess sucess: {}", res);
                    Ok(())
                }
            }
        }
    }
    /// 获取年龄
    /// 调用前请调用process方法
    ///```
    ///  use arc_lib::sdk::{ArcEngine, ArcEngine_U};
    ///  let engine= ArcEngine_U::init_engine().unwrap();
    ///  let engine= ArcEngine::init_engine(&engine);
    ///  engine|{engine.process(img, face_info).map(||engine.age().map(|age|println!("age is {}",age))) ;
    ///
    ///```
    pub fn age(&self) -> FaceResult<i32> {
        let age_info = ASF_AgeInfo { ageArray: ptr::null_mut(), num: 0 };
        unsafe {
            // 获取年龄
            let res = ASFGetAge(*self.engine, &age_info as *const ASF_AgeInfo as *mut ASF_AgeInfo);
            if res != MOK {
                Err(format!("ASFGetAge fail: {}", res))
            } else {
                let ageArray = ptr::slice_from_raw_parts(age_info.ageArray, age_info.num as usize);
                if { &*ageArray }.len() > 0 {
                    Ok({ &*ageArray }[0])
                } else {
                    Err(format!("not check Age"))
                }
            }
        }
    }
    /// 获取性别
    /// 调用前请调用process方法
    ///```
    ///  use arc_lib::sdk::{ArcEngine, ArcEngine_U};
    ///  let engine= ArcEngine_U::init_engine().unwrap();
    ///  let engine= ArcEngine::init_engine(&engine) ;
    ///   engine.process(img, face_info).map(||engine.gender().map(|gender|println!("gender is {}",gender))) ;
    ///
    ///```
    pub fn gender(&self) -> FaceResult<i32> {
        let gender_info = ASF_GenderInfo { genderArray: ptr::null_mut(), num: 0 };
        unsafe {
            // 获取年龄
            let res = ASFGetGender(*self.engine, &gender_info as *const ASF_GenderInfo as *mut ASF_GenderInfo);
            if res != MOK {
                Err(format!("Get Gender fail: {}", res))
            } else {
                let gender_array = ptr::slice_from_raw_parts(gender_info.genderArray, gender_info.num as usize);
                if { &*gender_array }.len() > 0 {
                    println!("Gender: {}", { &*gender_array }[0]);
                    Ok({ &*gender_array }[0])
                } else {
                    Err(format!("not check gender"))
                }
            }
        }
    }
    /// 获取3D角度
    /// 调用前请调用process方法
    ///```
    ///  use arc_lib::sdk::{ArcEngine, ArcEngine_U};
    ///  let engine= ArcEngine_U::init_engine().unwrap();
    ///  let engine= ArcEngine::init_engine(&engine);
    ///   engine.process(img, face_info).map(||engine.gender().map(|gender|println!("gender is {}",gender))) ;
    ///
    ///```
    pub fn angle(&self) -> FaceResult<Face3DAngle> {
        let angle_info = ASF_Face3DAngle { roll: ptr::null_mut(), yaw: ptr::null_mut(), pitch: ptr::null_mut(), status: ptr::null_mut(), num: 0 };
        unsafe {
            let res = ASFGetFace3DAngle(*self.engine, &angle_info as *const ASF_Face3DAngle as *mut ASF_Face3DAngle);
            if res != MOK {
                Err(format!("ASFGet Face3DAngle fail: {}", res))
            } else {
                let roll = ptr::slice_from_raw_parts(angle_info.roll, angle_info.num as usize);
                let yaw = ptr::slice_from_raw_parts(angle_info.yaw, angle_info.num as usize);
                let pitch = ptr::slice_from_raw_parts(angle_info.pitch, angle_info.num as usize);
                if { &*roll }.len() > 0 {
                    println!("3DAngle roll: {}   yaw: {}   pitch: {}", { &*roll }[0], { &*yaw }[0], { &*pitch }[0]);
                    let roll = { &*roll }[0];
                    let yaw = { &*yaw }[0];
                    let pitch = { &*pitch }[0];
                    Ok(Face3DAngle { roll, yaw, pitch })
                } else {
                    Err(format!("not check gender"))
                }
            }
        }
    }

    ///检测活体信息
    pub fn live(&mut self) -> FaceResult<i32> {
        let rgb_liveness_info = ASF_LivenessInfo { isLive: ptr::null_mut(), num: 0 };
        unsafe {
            //获取RGB活体信息
            let res = ASFGetLivenessScore(*self.engine, &rgb_liveness_info as *const ASF_LivenessInfo as *mut ASF_LivenessInfo);
            if res != MOK {
                Err(format!(" Get Liveness Score fail: {}", res))
            } else {
                let lives = ptr::slice_from_raw_parts(rgb_liveness_info.isLive, rgb_liveness_info.num as usize);
                if { &*lives }.len() > 0 {
                    Ok({ &*lives }[0])
                } else {
                    Err(format!("not check live info"))
                }
            }
        }
    }

    /// 人脸特征比对
    ///```
  ///   use arc_lib::sdk::{ArcEngine_U, ArcEngine, get_img_info};
  ///   let engine = ArcEngine_U::init_engine()?;
   ///  let engine = ArcEngine::init_engine(&engine);
   ///  let mut face1 = get_img_info("d:/zk-2.jpg")?;
   ///  let info1 = engine.face_check(&mut face1)?;
   ///  let feature1 = engine.face_feature(&mut face1, &info1[0])?;
    /// let mut face2 = get_img_info("d:/lz123.jpg")?;
    /// let info2 = engine.face_check(&mut face1)?;
   ///  let feature2 = engine.face_feature(&mut face1, &info2[0])?;
   ///  let facesimilar = engine.compare_feature(&feature1, &feature2)?;
    ///```
    pub fn compare_feature(&self, source_feature: &FaceFeature, target_feature: &FaceFeature) -> FaceResult<FaceSimilar> {
        // 单人脸特征比对
        let confidenceLevel: MFloat = 0.0;
        unsafe {
            let res = ASFFaceFeatureCompare(*self.engine, &source_feature.to_asf_face_feature() as *const ASF_FaceFeature as *mut ASF_FaceFeature, &target_feature.to_asf_face_feature() as *const ASF_FaceFeature as *mut ASF_FaceFeature, &confidenceLevel as *const MFloat as *mut MFloat, ASF_CompareModel_ASF_LIFE_PHOTO);
            if res != MOK {
                Err(format!("ASFFaceFeatureCompare fail: {}", res))
            } else {
                println!("ASFFaceFeatureCompare sucess: {}", confidenceLevel);
                Ok(FaceSimilar { score: confidenceLevel })
            }
        }
    }
}

#[derive(Copy, Clone, Serialize, Deserialize)]
pub struct FaceSimilar {
    score: MFloat,
}

impl FaceSimilar {
    pub fn new() -> Self {
        Self { score: 0.0 }
    }
}

impl Into<Vec<FaceInfo>> for ASF_MultiFaceInfo {
    fn into(self) -> Vec<FaceInfo> {
        let mut result = vec![];
        let face_infos = self;
        if face_infos.faceNum > 0 {
            unsafe {
                let faces = ptr::slice_from_raw_parts(face_infos.faceRect, face_infos.faceNum as usize);
                let faces = &*faces;
                for i in 0..face_infos.faceNum as usize {
                    let mut single_faces = FaceInfo::new();
                    single_faces.faceRect.left = faces[i].left;
                    single_faces.faceRect.top = faces[i].top;
                    single_faces.faceRect.right = faces[i].right;
                    single_faces.faceRect.bottom = faces[i].bottom;
                    let orients = ptr::slice_from_raw_parts(face_infos.faceOrient, face_infos.faceNum as usize);
                    single_faces.orient = { &*orients }[i];
                    result.push(single_faces);
                }
            }
        };
        result
    }
}

impl From<&Vec<FaceInfo>> for ASF_MultiFaceInfo {
    fn from(infos: &Vec<FaceInfo>) -> Self {
        let mut rects = Box::new(vec![]);
        let mut orients = Box::new(vec![]);
        for f in infos {
            rects.push(MRECT {
                left: f.faceRect.left,
                top: f.faceRect.top,
                right: f.faceRect.right,
                bottom: f.faceRect.bottom,
            });
            orients.push(f.orient);
        }

        let mut rects = Box::into_raw(rects);
        let mut orients = Box::into_raw(orients);
        ASF_MultiFaceInfo {
            faceRect: rects as *mut MRECT,
            faceOrient: orients as *mut MInt32,
            faceNum: infos.len() as MInt32,
            faceID: Box::into_raw(Box::new(-1)),
        }
    }
}

#[test]
pub fn test_face_check() {
    let engine = ArcEngine_U::init_engine();
    engine.map(|mut engine| {
        let mut engine = ArcEngine::init_engine(&engine);
        println!("engine init success:  {:?}", &engine.engine);
        get_img_info("d:/zk-2.jpg").map(|mut m| {
            let face = engine.face_check(&mut m);
            face.map(|mf| {
                mf.into_iter().map(|sf| {
                    println!("ASFInitEngine over  {} ", serde_json::to_string(&sf).unwrap());
                    engine.face_feature(&mut m, &sf).map(|f| {
                        let feature_base64 = format!("{}", encode(&f.feature));
                        println!("feature: {}", feature_base64);
                        // println!("face check {} face",face.faceNum);
                        show_window_title(&m, &format!("rust opencv {} ", &feature_base64[0..20]));
                    });
                });
            });
        });
    });
}

#[test]
pub fn test_compare_feature() {
    fn compare_feature_demo() -> FaceResult<FaceSimilar> {
        let engine = ArcEngine_U::init_engine()?;
        let engine = ArcEngine::init_engine(&engine);
        let mut face1 = get_img_info("d:/zk-2.jpg")?;
        let info1 = engine.face_check(&mut face1)?;
        let feature1 = engine.face_feature(&mut face1, &info1[0])?;
        let mut face2 = get_img_info("d:/zk1.jpg")?;
        let info2 = engine.face_check(&mut face2)?;
        let feature2 = engine.face_feature(&mut face1, &info2[0])?;
        let facesimilar = engine.compare_feature(&feature1, &feature2)?;
        println!("score:{}", facesimilar.score);
        Ok(facesimilar)
    }
    compare_feature_demo().map_err(|e| println!("{}", e));
}

#[test]
pub fn test_init_engine() {
    let engine = ArcEngine_U::init_engine();
    engine.map(|engine| {
        let engine = ArcEngine::init_engine(&engine);
        println!("engine init success:  {:?}", &engine.engine);
    });
}

/// use opencv get image info
pub fn get_img_info(img_path: &str) -> FaceResult<core::Mat> {
    imgcodecs::imread(img_path, IMREAD_COLOR).or_else(|e| {
        Err(format!("opencv load image fail :{}", img_path))
    })
}

pub fn get_img_info_mem(img_data: Vec<u8>) -> FaceResult<core::Mat> {
    let mut data = Vector::from_iter(img_data);
    let data = VectorOfu8::from(data);
    imgcodecs::imdecode(&data, IMREAD_COLOR).or_else(|e| {
        Err(format!("opencv load image fail :{}", e))
    })
}

#[test]
pub fn test_get_img_info() {
    get_img_info("d:/zk-2.jpg").map(|m| {
        show_window(&m)
    });
}

fn show_window_title(mat: &Mat, window: &str) {
    named_window(window, 1);
    highgui::imshow(window, &mat);
    highgui::wait_key(0);
    highgui::destroy_window(window);
}

//裁剪图片
fn cut_ipl_image(src: &mut Mat, size: Size) -> Result<Mat, Error> {
    println!("dst size {:?}", size);
    Mat::roi(src, OpencvRect::new(0, 0, size.width, size.height))
}

fn show_window(mat: &Mat) {
    let window = "rust opencv ";
    named_window(window, 1);
    highgui::imshow(window, &mat);
    highgui::wait_key(0);
    highgui::destroy_window(window);
}

#[derive(Copy, Clone, Serialize, Deserialize)]
pub struct Rect {
    /**
     * 人脸矩形的最左边
     */
    pub left: i32,
    /**
     * 人脸矩形的最上边
     */
    pub top: i32,
    /**
     * 人脸矩形的最右边
     */
    pub right: i32,
    /**
     * 人脸矩形的最下边
     */
    pub bottom: i32,
}

impl Into<MRECT> for Rect {
    fn into(self) -> MRECT {
        MRECT {
            left: self.left,
            top: self.top,
            right: self.right,
            bottom: self.bottom,
        }
    }
}

#[derive(Serialize, Deserialize, Clone)]
pub struct FaceInfo {
    //人脸位置信息
    pub faceRect: Rect,
    //人脸角度
    pub orient: MInt32,
    //-1  faceId，IMAGE模式下不返回faceId
    pub faceId: MInt32,
    // 有脸特征数据
    pub feature: Option<String>,
}


impl FaceInfo {
    pub fn new() -> Self {
        let faceRect = Rect { bottom: 0, left: 0, right: 0, top: 0 };
        FaceInfo { faceRect, orient: 0, faceId: -1, feature: None }
    }
}